/**
 * Copyright (c) 2012-2014 All Rights Reserved.
 */
package com.easy.framwork.utils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.easy.framwork.exceptions.ConvertException;
import com.easy.framwork.exceptions.ReflectException;
import com.easy.framwork.utils.bean.PropertyDescriptorsCache;

/**
 * 
 * @author wy
 * @version v 0.1 2014-1-2 下午10:08:14 wy Exp $
 */
public class BeanUtils {

    private final static Logger log = LoggerFactory.getLogger(BeanUtils.class);

    /** 日期格式化 */
    private static final String[] DATE_FORMATS = new String[] { "yyyy-MM-dd", "yyyy-MM",
                                                               "yyyy-MM-dd HH:mm:ss", "HH:mm:ss",
                                                               "yyyy-MM-dd HH:mm", "HH:mm" };

    /**
     * 单个对象转换
     * 
     * @param target 目标对象
     * @param map 源对象
     * 
     * @return 转换后的目标对象
     */
    public static <T> T convert(T target, Map<String, Object> map) {
        return convert(target, map, null);
    }

    /**
     * 单个对象转换
     * 
     * @param target 目标对象
     * @param map 源对象
     * 
     * @return 转换后的目标对象
     */
    public static <T> T convert(T target, Map<String, Object> map, String[] ignoreProperties) {

        //过滤的属性
        List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties)
            : new ArrayList<String>();

        //获取目标对象属性信息
        Field[] fields = getFields(target.getClass());

        for (Field field : fields) {

            try {
                if (!ignoreList.isEmpty() && ignoreList.contains(field.getName())) {
                    continue;
                }

                Object value = null;

                //map的key
                String mapKey = field.getName();

                if (map.containsKey(mapKey)) {
                    value = map.get(mapKey);
                }

                PropertyDescriptor targetPd = getPropertyDescriptor(target.getClass(),
                    field.getName());

                if (targetPd.getWriteMethod() == null) {
                    continue;
                }

                Method writeMethod = targetPd.getWriteMethod();

                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                    writeMethod.setAccessible(true);
                }

                writeMethod.invoke(target, value);
            }
            catch (Exception e) {
                throw new ConvertException(e);
            }
        }

        return target;
    }

    /**
     * 单个对象转换
     * 
     * @param target 目标对象
     * @param source 源对象
     * 
     * @return 转换后的目标对象
     */
    public static <T> T convert(T target, Object source) {
        return convert(target, source, null);
    }

    /**
     * 单个对象转换
     * 
     * @param target 目标对象
     * @param source 源对象
     * @param ignoreProperties 需要过滤的属性
     * 
     * @return 转换后的目标对象
     */
    public static <T> T convert(T target, Object source, String[] ignoreProperties) {

        //过滤的属性
        List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties)
            : new ArrayList<String>();

        //获取目标对象属性信息
        PropertyDescriptor[] targetPds = getPropertyDescriptors(target.getClass());
        for (PropertyDescriptor targetPd : targetPds) {
            try {
                if (targetPd.getWriteMethod() == null
                    || (!ignoreList.isEmpty() && ignoreList.contains(targetPd.getName()))) {
                    continue;
                }
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(),
                    targetPd.getName());
                if (sourcePd != null && sourcePd.getReadMethod() != null) {

                    Method readMethod = sourcePd.getReadMethod();
                    if (!Modifier.isPublic(readMethod.getModifiers())) {
                        readMethod.setAccessible(true);
                    }
                    Object value = readMethod.invoke(source);

                    Method writeMethod = targetPd.getWriteMethod();

                    if (!Modifier.isPublic(writeMethod.getModifiers())) {
                        writeMethod.setAccessible(true);
                    }

                    writeMethod.invoke(target, value);
                }
            }
            catch (Exception e) {
                throw new ConvertException(e);
            }

        }
        return target;
    }

    /**
     * string类型转换
     * 
     * @param s
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T convert(String s, Class<T> clazz) {

        if (StringUtils.isBlank(s)) {
            if (clazz.isPrimitive()) {
                throw new ReflectException("class:" + clazz + "必须要有一个默认的值");
            }
            return null;
        }
        
        if (clazz == String.class || clazz == Object.class) {
            return (T) s;
        }
        s = s.trim();
        Object result = null;
        if (clazz == Integer.class || clazz == int.class) {
            result = Integer.parseInt(s);
        } else if (clazz == Long.class || clazz == long.class) {
            result = Long.parseLong(s);
        } else if (clazz == Double.class || clazz == double.class) {
            result = Double.parseDouble(s);
        } else if (clazz == Short.class || clazz == short.class) {
            result = Short.parseShort(s);
        } else if (clazz == Float.class || clazz == float.class) {
            result = Float.parseFloat(s);
        } else if (clazz == Boolean.class) {
            result = Boolean.parseBoolean(s) || "1".equals(s);
        } else if (clazz == java.math.BigDecimal.class) {
            result = new java.math.BigDecimal(s);
        } else if (clazz == java.math.BigInteger.class) {
            result = new java.math.BigInteger(s);
        } else if (clazz == byte[].class) {
            result = s.getBytes();
        } else if (clazz == java.util.Date.class) {
            result = DateUtils.parseDateStrictly(s, DATE_FORMATS);
        } else if (clazz == java.sql.Date.class) {
            result = new java.sql.Date(DateUtils.parseDateStrictly(s, DATE_FORMATS).getTime());
        } else if (clazz == java.sql.Time.class) {
            result = java.sql.Time.valueOf(s);
        } else if (clazz == java.sql.Timestamp.class) {
            result = java.sql.Timestamp.valueOf(s);
        } else if (clazz == byte.class || clazz == Byte.class) {
            result = Byte.valueOf(s);
        } else {
            Class<?> loadClazz = null;
            try {
                loadClazz = Class.forName(s);
            }
            catch (Exception e) {
                log.warn("不能加载[" + s + "]类，转换失败");
            }
            if (loadClazz != null && clazz.isAssignableFrom(loadClazz)) {
                result = ReflectUtils.newInstance(loadClazz);
            } else {
                throw new ConvertException("不能够将[" + s + "]转换为" + clazz + "类型");
            }
        }
        return (T) result;
    }

    /**
     * 返回JavaBean所有字段信息
     * 
     * @param beanClass the bean class
     * @return the field [ ]
     */
    private static Field[] getFields(Class<?> beanClass) {
        PropertyDescriptorsCache cache = PropertyDescriptorsCache.forClass(beanClass);
        return cache.getFields();
    }

    /**
     * 返回JavaBean给定JavaBean给定属性的 <code>PropertyDescriptors</code>
     * 
     * @param beanClass the bean class
     * @param propertyName the name of the property
     * @return the corresponding PropertyDescriptor, or <code>null</code> if
     *         none
     */
    private static PropertyDescriptor getPropertyDescriptor(Class<?> beanClass, String propertyName) {

        PropertyDescriptorsCache cache = PropertyDescriptorsCache.forClass(beanClass);
        return cache.getPropertyDescriptor(propertyName);
    }

    /**
     * 返回JavaBean所有属性的<code>PropertyDescriptor</code>
     * 
     * @param beanClass the bean class
     * @return the property descriptor [ ]
     */
    private static PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass) {

        PropertyDescriptorsCache cache = PropertyDescriptorsCache.forClass(beanClass);
        return cache.getPropertyDescriptors();
    }

}
